<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=2"><meta name="theme-color" content="#FFF"><meta name="baidu-site-verification" content="code-DvHFUp3jdk"><meta name="baidu-site-verification" content="code-eHnRZZHAeP"><meta name="baidu-site-verification" content="code-EmlZBboqEW"><meta name="baidu-site-verification" content="code-Hf9ZXEHZ41"><meta name="360-site-verification" content="941073b91e9afaf02748ba000ba47e80"><meta name="360-site-verification" content="010bd45f0d523ce46f298dd597ef0289"><meta name="sogou_site_verification" content="hlLPNfmIjv"><meta name="sogou_site_verification" content="y3La2dTB20"><meta name="msvalidate.01" content="03829B28CC46A7AA4B5503897F475BA1"><script>!function(){var s="https://s.ssl.qhres2.com/ssl/ab77b6ea7f3fbf79.js";document.write('<script src="'+s+'" id="sozz"><\/script>')}()</script><script>!function(t,e,c,n,r,a,s){t[c]=t[c]||function(){(t[c].q=t[c].q||[]).push(arguments)},a=e.createElement(n),a.async=1,a.src="https://www.clarity.ms/tag/"+r+"?ref=bwt",s=e.getElementsByTagName(n)[0],s.parentNode.insertBefore(a,s)}(window,document,"clarity","script","8xwpfccwj2")</script><script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?1d318b0509ca0909017253ed6f659ae4";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?aea6c1471d6299bdd1c87a0836f18675";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?498ca413655655a62058d6b5d7c62786";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?e19e8161e13f718806b79f49240583b4";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon.png"><link rel="icon" type="image/ico" sizes="32x32" href="/images/favicon.ico"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><meta name="google-site-verification" content="UA-139249935-1"><meta name="baidu-site-verification" content="cOtBu9CtTEIuwJeN"><link rel="alternate" type="application/rss+xml" title="了尘兰若的小坑" href="https://liaochenlanruo.gitee.io/rss.xml"><link rel="alternate" type="application/atom+xml" title="了尘兰若的小坑" href="https://liaochenlanruo.gitee.io/atom.xml"><link rel="alternate" type="application/json" title="了尘兰若的小坑" href="https://liaochenlanruo.gitee.io/feed.json"><link rel="stylesheet" href="//fonts.googleapis.com/css?family=Mulish:300,300italic,400,400italic,700,700italic%7CFredericka%20the%20Great:300,300italic,400,400italic,700,700italic%7CNoto%20Serif%20JP:300,300italic,400,400italic,700,700italic%7CNoto%20Serif%20SC:300,300italic,400,400italic,700,700italic%7CInconsolata:300,300italic,400,400italic,700,700italic&display=swap&subset=latin,latin-ext"><link rel="stylesheet" href="/css/app.css?v=0.2.5"><meta name="keywords" content="编程,Shiny入门系列"><link rel="canonical" href="https://liaochenlanruo.gitee.io/post/72d6.html"><title>Shiny从入门到入定——9-上传和下载 - IT | ResearchGo = 了尘兰若的小坑 = liaochenlanruo</title><meta name="generator" content="Hexo 5.4.0"></head><body itemscope itemtype="http://schema.org/WebPage"><div id="loading"><div class="cat"><div class="body"></div><div class="head"><div class="face"></div></div><div class="foot"><div class="tummy-end"></div><div class="bottom"></div><div class="legs left"></div><div class="legs right"></div></div><div class="paw"><div class="hands left"></div><div class="hands right"></div></div></div></div><div id="container"><header id="header" itemscope itemtype="http://schema.org/WPHeader"><div class="inner"><div id="brand"><div class="pjax"><h1 itemprop="name headline">Shiny从入门到入定——9-上传和下载</h1><div class="meta"><span class="item" title="Created: 2024-04-27 11:01:58"><span class="icon"><i class="ic i-calendar"></i> </span><span class="text">Posted on</span> <time itemprop="dateCreated datePublished" datetime="2024-04-27T11:01:58+08:00">2024-04-27</time> </span><span class="item" title="Symbols count in article"><span class="icon"><i class="ic i-pen"></i> </span><span class="text">Symbols count in article</span> <span>8.8k</span> <span class="text">words</span> </span><span class="item" title="Reading time"><span class="icon"><i class="ic i-clock"></i> </span><span class="text">Reading time</span> <span>8 mins.</span></span></div></div></div><nav id="nav"><div class="inner"><div class="toggle"><div class="lines" aria-label="Toggle navigation bar"><span class="line"></span> <span class="line"></span> <span class="line"></span></div></div><ul class="menu"><li class="item title"><a href="/" rel="start">ResearchGo</a></li></ul><ul class="right"><li class="item theme"><i class="ic i-sun"></i></li><li class="item search"><i class="ic i-search"></i></li></ul></div></nav></div><div id="imgs" class="pjax"><img src="https://cdn.jsdelivr.net/gh/liaochenlanruo/cdn@master/img/custom/bgs/thumb_209.webp"></div></header><div id="waves"><svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto"><defs><path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z"/></defs><g class="parallax"><use xlink:href="#gentle-wave" x="48" y="0"/><use xlink:href="#gentle-wave" x="48" y="3"/><use xlink:href="#gentle-wave" x="48" y="5"/><use xlink:href="#gentle-wave" x="48" y="7"/></g></svg></div><main><div class="inner"><div id="main" class="pjax"><div class="article wrap"><div class="breadcrumb" itemscope itemtype="https://schema.org/BreadcrumbList"><i class="ic i-home"></i> <span><a href="/">Home</a></span><i class="ic i-angle-right"></i> <span class="current" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem"><a href="/categories/IT/" itemprop="item" rel="index" title="In IT"><span itemprop="name">IT</span></a><meta itemprop="position" content="1"></span></div><article itemscope itemtype="http://schema.org/Article" class="post block" lang="en"><link itemprop="mainEntityOfPage" href="https://liaochenlanruo.gitee.io/post/72d6.html"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="image" content="/images/head.jpg"><meta itemprop="name" content="Hualin Liu"><meta itemprop="description" content="liaochenlanruo, 分享微生物生物信息学分析方法，欢迎加入QQ群交流945751012，不接受群内广告！"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="了尘兰若的小坑"></span><div class="body md" itemprop="articleBody"><div class="gallery" itemscope itemtype="http://schema.org/ImageGallery"><img data-src="https://cdn.jsdelivr.net/gh/liaochenlanruo/cdn@master/img/custom/bgs/thumb_209.webp" itemprop="contentUrl"></div><h1 id="9-上传和下载"><a class="anchor" href="#9-上传和下载">#</a> 9 上传和下载</h1><p>在应用中，用户处上传和下载文件是一项常用功能。你可以用它来上传数据进行分析，或者将结果作为数据集或报告下载。本章将介绍在应用中进行文件上传和下载所需的用户界面和服务器组件。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>library<span class="token punctuation">(</span>shiny<span class="token punctuation">)</span></pre></td></tr></table></figure><h2 id="91-上传"><a class="anchor" href="#91-上传">#</a> 9.1 上传</h2><p>我们先从文件上传开始讨论，向你展示基本的 UI 和 server 组件，然后展示它们如何在简单应用中协同工作。</p><h3 id="911-用户界面-ui"><a class="anchor" href="#911-用户界面-ui">#</a> 9.1.1 用户界面 UI</h3><p>支持文件上传的用户界面很简单：只需在用户界面中添加<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vZmlsZUlucHV0Lmh0bWw="> fileInput ()</span> 即可。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  fileInput<span class="token punctuation">(</span><span class="token string">"upload"</span><span class="token punctuation">,</span> <span class="token string">"Upload a file"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure><p>与其他大多数 UI 组件一样，这里只需要两个必需参数： <code>id</code> 和 <code>label</code> 。 <code>width</code> 、 <code>buttonLabel</code> 和 <code>placeholder</code> 参数允许你以其他方式调整外观。我不会在这里讨论它们，但你可以在<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vZmlsZUlucHV0Lmh0bWw=">？fileInput</span> 中了解更多。</p><h3 id="912-服务器-server"><a class="anchor" href="#912-服务器-server">#</a> 9.1.2 服务器 server</h3><p>在 server 上处理 <code>fileInput()</code> 比处理其他输入稍微复杂一些。大多数输入返回简单的向量，但 <code>fileInput()</code> 返回一个包含四列的数据框：</p><ul><li><p><code>name</code> ：用户计算机上的原始文件名。</p></li><li><p><code>size</code> ：文件大小（以字节为单位）。默认情况下，用户只能上传最多 5MB 的文件。你可以在启动 Shiny 之前设置 <code>shiny.maxRequestSize</code> 选项来增加文件大小限制。例如，要允许最多 10MB，可以运行 <code>options(shiny.maxRequestSize = 10 * 1024^2)</code> 。</p></li><li><p><code>type</code> ：文件的 <code>“MIME type”</code> 。这是文件类型的正式规范，通常根据文件扩展名得出，在 Shiny 应用中很少需要。</p></li><li><p><code>datapath</code> ：数据在 server 上上传到的路径。请将此路径视为临时路径：如果用户上传更多文件，此文件可能会被删除。数据始终保存到临时目录，并赋予临时名称。</p></li></ul><p>作者认为理解这种数据结构最简单的方法是制作一个简单的应用。运行以下代码并上传几个文件，以了解 Shiny 提供的数据。在 <code>图9.1</code> 中，你可以看到我上传了几张小狗照片（来自第 7.3 节）后的结果。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  fileInput<span class="token punctuation">(</span><span class="token string">"upload"</span><span class="token punctuation">,</span> <span class="token keyword">NULL</span><span class="token punctuation">,</span> buttonLabel <span class="token operator">=</span> <span class="token string">"Upload..."</span><span class="token punctuation">,</span> multiple <span class="token operator">=</span> <span class="token boolean">TRUE</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  tableOutput<span class="token punctuation">(</span><span class="token string">"files"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>  output<span class="token operator">$</span>files <span class="token operator">&lt;-</span> renderTable<span class="token punctuation">(</span>input<span class="token operator">$</span>upload<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p><img data-src="https://d33wubrfki0l68.cloudfront.net/31fddd4b87d0e36141c7b8cbb2dd240f8c2d26d1/026fa/demos/action-transfer/upload.png" alt="图9.1 这个简单的应用可以让你看到Shiny为上传的文件提供了哪些数据。可以在 https://hadley.shinyapps.io/ms-upload 查看实时效果。"></p><p>请注意，我使用了 <code>label</code> 和 <code>buttonLabel</code> 参数来微调外观，以及 <code>multiple = TRUE</code> 来允许用户上传多个文件。</p><h3 id="913-上传数据"><a class="anchor" href="#913-上传数据">#</a> 9.1.3 上传数据</h3><p>如果用户正在上传数据集，你需要注意以下两个细节：</p><ul><li><p><code>input$upload</code> 在页面加载时被初始化为 <code>NULL</code> ，因此你需要使用 <code>req(input$upload)</code> 来确保你的代码等待第一个文件上传。</p></li><li><p><code>accept</code> 参数允许你限制可能的输入。最简单的方法是提供一个文件扩展名的字符向量，如 <code>accept = &quot;.csv&quot;</code> 。但是，accept 参数只是对浏览器的建议，并不总是强制执行，因此自行验证是个好习惯（例如第 8.1 节）。在 R 中获取文件扩展名最简单的方法是使用<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3IvdG9vbHMvZmlsZXV0aWxzLmh0bWw="> tools::file_ext ()</span>，但要注意它会删除扩展名前面的 <code>.</code> 。</p></li></ul><p>将这些想法结合起来，我们得到以下应用，你可以上传一个 <code>.csv</code> 或 <code>.tsv</code> 文件，并查看前 <code>n</code> 行。你可以在 <span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLXVwbG9hZC12YWxpZGF0ZQ==">https://hadley.shinyapps.io/ms-upload-validate</span> 查看它的运行情况。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  fileInput<span class="token punctuation">(</span><span class="token string">"upload"</span><span class="token punctuation">,</span> <span class="token keyword">NULL</span><span class="token punctuation">,</span> accept <span class="token operator">=</span> c<span class="token punctuation">(</span><span class="token string">".csv"</span><span class="token punctuation">,</span> <span class="token string">".tsv"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  numericInput<span class="token punctuation">(</span><span class="token string">"n"</span><span class="token punctuation">,</span> <span class="token string">"Rows"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span> step <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  tableOutput<span class="token punctuation">(</span><span class="token string">"head"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre></pre></td></tr><tr><td data-num="7"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>  data <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    req<span class="token punctuation">(</span>input<span class="token operator">$</span>upload<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    </pre></td></tr><tr><td data-num="11"></td><td><pre>    ext <span class="token operator">&lt;-</span> tools<span class="token operator">::</span>file_ext<span class="token punctuation">(</span>input<span class="token operator">$</span>upload<span class="token operator">$</span>name<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre>    switch<span class="token punctuation">(</span>ext<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="13"></td><td><pre>      csv <span class="token operator">=</span> vroom<span class="token operator">::</span>vroom<span class="token punctuation">(</span>input<span class="token operator">$</span>upload<span class="token operator">$</span>datapath<span class="token punctuation">,</span> delim <span class="token operator">=</span> <span class="token string">","</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="14"></td><td><pre>      tsv <span class="token operator">=</span> vroom<span class="token operator">::</span>vroom<span class="token punctuation">(</span>input<span class="token operator">$</span>upload<span class="token operator">$</span>datapath<span class="token punctuation">,</span> delim <span class="token operator">=</span> <span class="token string">"\t"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="15"></td><td><pre>      validate<span class="token punctuation">(</span><span class="token string">"Invalid file; Please upload a .csv or .tsv file"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="16"></td><td><pre>    <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="17"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="18"></td><td><pre>  </pre></td></tr><tr><td data-num="19"></td><td><pre>  output<span class="token operator">$</span>head <span class="token operator">&lt;-</span> renderTable<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="20"></td><td><pre>    head<span class="token punctuation">(</span>data<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> input<span class="token operator">$</span>n<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="21"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="22"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p>请注意，由于 <code>multiple = FALSE</code> （默认值）， <code>input$file</code> 将是一个单行数据框，而 <code>input$file$name</code> 和 <code>input$file$datapath</code> 将是一个长度为 1 的字符向量。</p><h2 id="92-下载"><a class="anchor" href="#92-下载">#</a> 9.2 下载</h2><p>接下来，我们将讨论文件下载，向你展示基本的用户界面和服务器组件，然后演示如何使用它们来允许用户下载数据或报告。</p><h3 id="921-基本操作"><a class="anchor" href="#921-基本操作">#</a> 9.2.1 基本操作</h3><p>同样，用户界面非常简单：使用 <code>downloadButton(id)</code> 或 <code>downloadLink(id)</code> 为用户提供一个可点击的下载文件选项。结果如 <code>图9.2</code> 所示。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  downloadButton<span class="token punctuation">(</span><span class="token string">"download1"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  downloadLink<span class="token punctuation">(</span><span class="token string">"download2"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure><p><img data-src="https://d33wubrfki0l68.cloudfront.net/24e3c954a6c32523d02cd22597227ffb7b77313e/b15ad/demos/action-transfer/download.png" alt="图9.2 一个下载按钮和一个下载链接"></p><p>你可以使用与 <code>actionButtons()</code> 相同的 <code>class</code> 和 <code>icon</code> 参数来自定义它们的外观，如第 2.2.7 节所述。</p><p>与其他输出不同，<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vZG93bmxvYWRCdXR0b24uaHRtbA==">downloadButton()</span> 不与渲染函数配对。相反，你使用<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vZG93bmxvYWRIYW5kbGVyLmh0bWw="> downloadHandler ()</span>，它看起来像这样：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>output<span class="token operator">$</span>download <span class="token operator">&lt;-</span> downloadHandler<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  filename <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    paste0<span class="token punctuation">(</span>input<span class="token operator">$</span>dataset<span class="token punctuation">,</span> <span class="token string">".csv"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="5"></td><td><pre>  content <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>file<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    write.csv<span class="token punctuation">(</span>data<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> file<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure><p><code>downloadHandler()</code> 有两个参数，都是函数：</p><ul><li><p><code>filename</code> 应该是一个没有参数的函数，返回一个文件名（字符串）。这个函数的工作是创建将在下载对话框中显示给用户的文件名。</p></li><li><p><code>content</code> 应该是一个带有一个参数 <code>file</code> 的函数，file 是保存文件的路径。这个函数的工作是将文件保存在 Shiny 知道的位置，以便可以将其发送给用户。</p></li></ul><p>这是一个不寻常的接口，但它允许 Shiny 控制文件应该保存的位置（以便可以将其放置在安全位置），同时你仍然可以控制文件的内容。</p><p>接下来，我们将把这些部分组合在一起，展示如何将数据文件或报告传输给用户。</p><h3 id="922-下载数据"><a class="anchor" href="#922-下载数据">#</a> 9.2.2 下载数据</h3><p>以下应用通过允许你下载 <code>datasets包</code> 中的任何数据集作为制表符分隔的文件，展示了数据下载的基础知识，如 <code>图9.3</code> 所示。我建议使用 <code>.tsv</code> （制表符分隔值）而不是 <code>.csv</code> （逗号分隔值），因为许多欧洲国家使用逗号来分隔数字的整数部分和小数部分（例如 <code>1,23</code> 与 <code>1.23</code> ）。这意味着它们不能使用逗号来分隔字段，而是在所谓的 <code>“c”sv</code> 文件中使用分号！通过使用制表符分隔的文件，你可以避免这种复杂性，因为它们在任何地方的工作方式都是相同的。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  selectInput<span class="token punctuation">(</span><span class="token string">"dataset"</span><span class="token punctuation">,</span> <span class="token string">"Pick a dataset"</span><span class="token punctuation">,</span> ls<span class="token punctuation">(</span><span class="token string">"package:datasets"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  tableOutput<span class="token punctuation">(</span><span class="token string">"preview"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  downloadButton<span class="token punctuation">(</span><span class="token string">"download"</span><span class="token punctuation">,</span> <span class="token string">"Download .tsv"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre></pre></td></tr><tr><td data-num="7"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>  data <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    out <span class="token operator">&lt;-</span> get<span class="token punctuation">(</span>input<span class="token operator">$</span>dataset<span class="token punctuation">,</span> <span class="token string">"package:datasets"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>is.data.frame<span class="token punctuation">(</span>out<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>      validate<span class="token punctuation">(</span>paste0<span class="token punctuation">(</span><span class="token string">"'"</span><span class="token punctuation">,</span> input<span class="token operator">$</span>dataset<span class="token punctuation">,</span> <span class="token string">"' is not a data frame"</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="13"></td><td><pre>    out</pre></td></tr><tr><td data-num="14"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre>  </pre></td></tr><tr><td data-num="16"></td><td><pre>  output<span class="token operator">$</span>preview <span class="token operator">&lt;-</span> renderTable<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="17"></td><td><pre>    head<span class="token punctuation">(</span>data<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="18"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="19"></td><td><pre>    </pre></td></tr><tr><td data-num="20"></td><td><pre>  output<span class="token operator">$</span>download <span class="token operator">&lt;-</span> downloadHandler<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="21"></td><td><pre>    filename <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="22"></td><td><pre>      paste0<span class="token punctuation">(</span>input<span class="token operator">$</span>dataset<span class="token punctuation">,</span> <span class="token string">".tsv"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="23"></td><td><pre>    <span class="token punctuation">&#125;</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="24"></td><td><pre>    content <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>file<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="25"></td><td><pre>      vroom<span class="token operator">::</span>vroom_write<span class="token punctuation">(</span>data<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> file<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="26"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="27"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="28"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p><img data-src="https://d33wubrfki0l68.cloudfront.net/eaecc41d850e5ddb2e0258dc33b939b249add5f3/800ce/demos/action-transfer/download-data.png" alt="图9.3 一个更丰富的应用，允许你选择内置的数据集并在下载前预览。可以在https://hadley.shinyapps.io/ms-download-data查看实时效果。"></p><p>注意，这里使用了<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vdmFsaWRhdGUuaHRtbA=="> validate ()</span> 来仅允许用户下载是数据框的数据集。更好的做法是对列表进行预过滤，但这样可以让您看到 <code>validate()</code> 的另一个应用。</p><h3 id="923-下载报告"><a class="anchor" href="#923-下载报告">#</a> 9.2.3 下载报告</h3><p>除了下载数据外，您可能还希望允许您的应用下载报告，该报告总结了 Shiny 应用中交互式探索的结果。这是一项需要大量工作的事情，因为您还需要以不同的格式显示相同的信息，但对于高风险的应用来说，这是非常有用的。</p><p>生成此类报告的一种强大方法是使用<span class="exturl" data-url="aHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duL3BhcmFtZXRlcml6ZWQtcmVwb3J0cy5odG1s">参数化的 RMarkdown 文档</span>。参数化的 RMarkdown 文件在 YAML 元数据中有一个 <code>params</code> 字段：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>title<span class="token operator">:</span> My Document</pre></td></tr><tr><td data-num="2"></td><td><pre>output<span class="token operator">:</span> html_document</pre></td></tr><tr><td data-num="3"></td><td><pre>params<span class="token operator">:</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  year<span class="token operator">:</span> <span class="token number">2018</span></pre></td></tr><tr><td data-num="5"></td><td><pre>  region<span class="token operator">:</span> Europe</pre></td></tr><tr><td data-num="6"></td><td><pre>  printcode<span class="token operator">:</span> <span class="token boolean">TRUE</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  data<span class="token operator">:</span> file.csv</pre></td></tr></table></figure><p>在文档中，您可以使用 <code>params$year</code> 、 <code>params$region</code> 等来引用这些值。YAML 元数据中的值是默认值；您通常通过在调用<span class="exturl" data-url="aHR0cHM6Ly9wa2dzLnJzdHVkaW8uY29tL3JtYXJrZG93bi9yZWZlcmVuY2UvcmVuZGVyLmh0bWw="> rmarkdown::render ()</span> 时提供 <code>params</code> 参数来覆盖它们。这使得从相同的 <code>.Rmd</code> 生成许多不同的报告变得容易。</p><p>以下是一个简单的示例，改编自<span class="exturl" data-url="aHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS9hcnRpY2xlcy9nZW5lcmF0aW5nLXJlcG9ydHMuaHRtbA=="> https://shiny.rstudio.com/articles/generating-reports.html</span>，该文章更详细地描述了这种技术。关键思想是从 <code>downloadHander()</code> 的 <code>content</code> 参数中调用 <code>rmarkdown::render()</code> 。如果您想生成其他输出格式，只需在 <code>.Rmd</code> 中更改输出格式，并确保更新扩展名（例如，更改为.pdf）。请在<span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLWRvd25sb2FkLXJtZA=="> https://hadley.shinyapps.io/ms-download-rmd</span> 查看实时效果。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  sliderInput<span class="token punctuation">(</span><span class="token string">"n"</span><span class="token punctuation">,</span> <span class="token string">"Number of points"</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  downloadButton<span class="token punctuation">(</span><span class="token string">"report"</span><span class="token punctuation">,</span> <span class="token string">"Generate report"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre></pre></td></tr><tr><td data-num="6"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  output<span class="token operator">$</span>report <span class="token operator">&lt;-</span> downloadHandler<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    filename <span class="token operator">=</span> <span class="token string">"report.html"</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    content <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>file<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>      params <span class="token operator">&lt;-</span> list<span class="token punctuation">(</span>n <span class="token operator">=</span> input<span class="token operator">$</span>n<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>      </pre></td></tr><tr><td data-num="12"></td><td><pre>      id <span class="token operator">&lt;-</span> showNotification<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="13"></td><td><pre>        <span class="token string">"Rendering report..."</span><span class="token punctuation">,</span> </pre></td></tr><tr><td data-num="14"></td><td><pre>        duration <span class="token operator">=</span> <span class="token keyword">NULL</span><span class="token punctuation">,</span> </pre></td></tr><tr><td data-num="15"></td><td><pre>        closeButton <span class="token operator">=</span> <span class="token boolean">FALSE</span></pre></td></tr><tr><td data-num="16"></td><td><pre>      <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="17"></td><td><pre>      on.exit<span class="token punctuation">(</span>removeNotification<span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">,</span> add <span class="token operator">=</span> <span class="token boolean">TRUE</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="18"></td><td><pre></pre></td></tr><tr><td data-num="19"></td><td><pre>      rmarkdown<span class="token operator">::</span>render<span class="token punctuation">(</span><span class="token string">"report.Rmd"</span><span class="token punctuation">,</span> </pre></td></tr><tr><td data-num="20"></td><td><pre>        output_file <span class="token operator">=</span> file<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="21"></td><td><pre>        params <span class="token operator">=</span> params<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="22"></td><td><pre>        envir <span class="token operator">=</span> new.env<span class="token punctuation">(</span>parent <span class="token operator">=</span> globalenv<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="23"></td><td><pre>      <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="24"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="25"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="26"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p>渲染一个 <code>.Rmd</code> 文件通常需要几秒钟的时间，因此这是使用第 8.2 节中提到的通知的好地方。</p><p>还有一些其他值得了解的技巧：</p><ul><li><p>RMarkdown 在当前工作目录中运行，这在许多部署场景中可能会失败（例如，在 shinyapps.io 上）。您可以通过在应用启动时将报告复制到临时目录来解决此问题（即在 server 函数外部）：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>report_path <span class="token operator">&lt;-</span> tempfile<span class="token punctuation">(</span>fileext <span class="token operator">=</span> <span class="token string">".Rmd"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre>file.copy<span class="token punctuation">(</span><span class="token string">"report.Rmd"</span><span class="token punctuation">,</span> report_path<span class="token punctuation">,</span> overwrite <span class="token operator">=</span> <span class="token boolean">TRUE</span><span class="token punctuation">)</span></pre></td></tr></table></figure><p>然后，在调用 <code>rmarkdown::render()</code> 时，将 <code>&quot;report.Rmd&quot;</code> 替换为 <code>report_path</code> ：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>rmarkdown<span class="token operator">::</span>render<span class="token punctuation">(</span>report_path<span class="token punctuation">,</span> </pre></td></tr><tr><td data-num="2"></td><td><pre>  output_file <span class="token operator">=</span> file<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  params <span class="token operator">=</span> params<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  envir <span class="token operator">=</span> new.env<span class="token punctuation">(</span>parent <span class="token operator">=</span> globalenv<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure></li><li><p>默认情况下，RMarkdown 会在当前进程中渲染报告，这意味着它将继承 Shiny 应用的许多设置（如加载的包、选项等）。为了获得更大的健壮性，我建议使用 <code>callr包</code> 在单独的 R 会话中运行 <code>render()</code> ：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>render_report <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> params<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  rmarkdown<span class="token operator">::</span>render<span class="token punctuation">(</span>input<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    output_file <span class="token operator">=</span> output<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    params <span class="token operator">=</span> params<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    envir <span class="token operator">=</span> new.env<span class="token punctuation">(</span>parent <span class="token operator">=</span> globalenv<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="8"></td><td><pre></pre></td></tr><tr><td data-num="9"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>  output<span class="token operator">$</span>report <span class="token operator">&lt;-</span> downloadHandler<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="11"></td><td><pre>    filename <span class="token operator">=</span> <span class="token string">"report.html"</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="12"></td><td><pre>    content <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>file<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="13"></td><td><pre>      params <span class="token operator">&lt;-</span> list<span class="token punctuation">(</span>n <span class="token operator">=</span> input<span class="token operator">$</span>slider<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre>      callr<span class="token operator">::</span>r<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="15"></td><td><pre>        render_report<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="16"></td><td><pre>        list<span class="token punctuation">(</span>input <span class="token operator">=</span> report_path<span class="token punctuation">,</span> output <span class="token operator">=</span> file<span class="token punctuation">,</span> params <span class="token operator">=</span> params<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="17"></td><td><pre>      <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="18"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="19"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="20"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure></li></ul><p>您可以在 Mastering Shiny GitHub 仓库中的<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2hhZGxleS9tYXN0ZXJpbmctc2hpbnkvdHJlZS9tYXN0ZXIvcm1hcmtkb3duLXJlcG9ydA=="> rmarkdown-report/</span> 文件夹中看到所有这些组件组合在一起。</p><p><span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vc2hpbnltZXRh">shinymeta</span> 包解决了一个相关的问题：有时您需要将 Shiny 应用的当前状态转换为可在未来重新运行的可重复报告。要了解更多信息，请查阅 Joe Cheng 在 useR! 2019 年主题演讲中介绍的 “<span class="exturl" data-url="aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj01S0J5UkM2ZXFDOA==">Shiny 的神圣目标：交互性与可重复性</span>”。</p><h2 id="93-案例研究"><a class="anchor" href="#93-案例研究">#</a> 9.3 案例研究</h2><p>最后，我们将通过一个简单的案例研究来结束本章。在这个案例中，我们将上传一个文件（用户可以指定分隔符），预览它，使用 Sam Firke 的<span class="exturl" data-url="aHR0cDovL3NmaXJrZS5naXRodWIuaW8vamFuaXRvcg=="> janitor</span> 包进行一些可选的转换，然后让用户将其下载为 <code>.tsv</code> 文件。</p><p>为了更容易地理解如何使用这个应用，我使用了<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vc2lkZWJhckxheW91dC5odG1s"> sidebarLayout ()</span> 来将应用分为三个主要步骤：</p><ul><li><p>上传和解析文件</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui_upload <span class="token operator">&lt;-</span> sidebarLayout<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  sidebarPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    fileInput<span class="token punctuation">(</span><span class="token string">"file"</span><span class="token punctuation">,</span> <span class="token string">"Data"</span><span class="token punctuation">,</span> buttonLabel <span class="token operator">=</span> <span class="token string">"Upload..."</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    textInput<span class="token punctuation">(</span><span class="token string">"delim"</span><span class="token punctuation">,</span> <span class="token string">"Delimiter (leave blank to guess)"</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    numericInput<span class="token punctuation">(</span><span class="token string">"skip"</span><span class="token punctuation">,</span> <span class="token string">"Rows to skip"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    numericInput<span class="token punctuation">(</span><span class="token string">"rows"</span><span class="token punctuation">,</span> <span class="token string">"Rows to preview"</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  <span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="8"></td><td><pre>  mainPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    h3<span class="token punctuation">(</span><span class="token string">"Raw data"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    tableOutput<span class="token punctuation">(</span><span class="token string">"preview1"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure></li><li><p>清理文件</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui_clean <span class="token operator">&lt;-</span> sidebarLayout<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  sidebarPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    checkboxInput<span class="token punctuation">(</span><span class="token string">"snake"</span><span class="token punctuation">,</span> <span class="token string">"Rename columns to snake case?"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    checkboxInput<span class="token punctuation">(</span><span class="token string">"constant"</span><span class="token punctuation">,</span> <span class="token string">"Remove constant columns?"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    checkboxInput<span class="token punctuation">(</span><span class="token string">"empty"</span><span class="token punctuation">,</span> <span class="token string">"Remove empty cols?"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>  <span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  mainPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    h3<span class="token punctuation">(</span><span class="token string">"Cleaner data"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    tableOutput<span class="token punctuation">(</span><span class="token string">"preview2"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure></li><li><p>下载文件</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui_download <span class="token operator">&lt;-</span> fluidRow<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  column<span class="token punctuation">(</span>width <span class="token operator">=</span> <span class="token number">12</span><span class="token punctuation">,</span> downloadButton<span class="token punctuation">(</span><span class="token string">"download"</span><span class="token punctuation">,</span> class <span class="token operator">=</span> <span class="token string">"btn-block"</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure><p>然后，这些文件被组合成一个单独的<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vZmx1aWRQYWdlLmh0bWw="> fluidPage ()</span>：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  ui_upload<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  ui_clean<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  ui_download</pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure><p>这种相同的组织方式使得理解应用程序变得更加容易。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  <span class="token comment"># Upload ---------------------------------------------------------</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  raw <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    req<span class="token punctuation">(</span>input<span class="token operator">$</span>file<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    delim <span class="token operator">&lt;-</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>input<span class="token operator">$</span>delim <span class="token operator">==</span> <span class="token string">""</span><span class="token punctuation">)</span> <span class="token keyword">NULL</span> <span class="token keyword">else</span> input<span class="token operator">$</span>delim</pre></td></tr><tr><td data-num="6"></td><td><pre>    vroom<span class="token operator">::</span>vroom<span class="token punctuation">(</span>input<span class="token operator">$</span>file<span class="token operator">$</span>datapath<span class="token punctuation">,</span> delim <span class="token operator">=</span> delim<span class="token punctuation">,</span> skip <span class="token operator">=</span> input<span class="token operator">$</span>skip<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>  output<span class="token operator">$</span>preview1 <span class="token operator">&lt;-</span> renderTable<span class="token punctuation">(</span>head<span class="token punctuation">(</span>raw<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> input<span class="token operator">$</span>rows<span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>  </pre></td></tr><tr><td data-num="10"></td><td><pre>  <span class="token comment"># Clean ----------------------------------------------------------</span></pre></td></tr><tr><td data-num="11"></td><td><pre>  tidied <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="12"></td><td><pre>    out <span class="token operator">&lt;-</span> raw<span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>    <span class="token keyword">if</span> <span class="token punctuation">(</span>input<span class="token operator">$</span>snake<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="14"></td><td><pre>      names<span class="token punctuation">(</span>out<span class="token punctuation">)</span> <span class="token operator">&lt;-</span> janitor<span class="token operator">::</span>make_clean_names<span class="token punctuation">(</span>names<span class="token punctuation">(</span>out<span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>    <span class="token keyword">if</span> <span class="token punctuation">(</span>input<span class="token operator">$</span>empty<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="17"></td><td><pre>      out <span class="token operator">&lt;-</span> janitor<span class="token operator">::</span>remove_empty<span class="token punctuation">(</span>out<span class="token punctuation">,</span> <span class="token string">"cols"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="18"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="19"></td><td><pre>    <span class="token keyword">if</span> <span class="token punctuation">(</span>input<span class="token operator">$</span>constant<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="20"></td><td><pre>      out <span class="token operator">&lt;-</span> janitor<span class="token operator">::</span>remove_constant<span class="token punctuation">(</span>out<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="21"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="22"></td><td><pre>    </pre></td></tr><tr><td data-num="23"></td><td><pre>    out</pre></td></tr><tr><td data-num="24"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="25"></td><td><pre>  output<span class="token operator">$</span>preview2 <span class="token operator">&lt;-</span> renderTable<span class="token punctuation">(</span>head<span class="token punctuation">(</span>tidied<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> input<span class="token operator">$</span>rows<span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="26"></td><td><pre>  </pre></td></tr><tr><td data-num="27"></td><td><pre>  <span class="token comment"># Download -------------------------------------------------------</span></pre></td></tr><tr><td data-num="28"></td><td><pre>  output<span class="token operator">$</span>download <span class="token operator">&lt;-</span> downloadHandler<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="29"></td><td><pre>    filename <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="30"></td><td><pre>      paste0<span class="token punctuation">(</span>tools<span class="token operator">::</span>file_path_sans_ext<span class="token punctuation">(</span>input<span class="token operator">$</span>file<span class="token operator">$</span>name<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">".tsv"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="31"></td><td><pre>    <span class="token punctuation">&#125;</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="32"></td><td><pre>    content <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>file<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="33"></td><td><pre>      vroom<span class="token operator">::</span>vroom_write<span class="token punctuation">(</span>tidied<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> file<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="34"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="35"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="36"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p><img data-src="https://d33wubrfki0l68.cloudfront.net/25874d076587129b1d92ceff28de69b7e5aec561/31f22/demos/action-transfer/case-study.png" alt="图9.4 一个允许用户上传文件、执行一些简单的清理操作，然后下载结果的应用程序。可访问 https://hadley.shinyapps.io/ms-case-study 查看实时效果。"></p></li></ul><h2 id="94-练习"><a class="anchor" href="#94-练习">#</a> 9.4 练习</h2><ol><li><p>使用 Thomas Lin Pedersen 的<span class="exturl" data-url="aHR0cHM6Ly9hbWJpZW50LmRhdGEtaW1hZ2luaXN0LmNvbS8="> ambient</span> 包生成<span class="exturl" data-url="aHR0cHM6Ly9hbWJpZW50LmRhdGEtaW1hZ2luaXN0LmNvbS9yZWZlcmVuY2Uvbm9pc2Vfd29ybGV5Lmh0bWw="> Worley 噪声</span>，并下载其 PNG 图像。</p></li><li><p>创建一个应用程序，允许用户上传 CSV 文件，选择一个变量，然后对该变量执行<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3Ivc3RhdHMvdC50ZXN0Lmh0bWw="> t.test ()</span>。在用户上传 CSV 文件后，您需要使用<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vdXBkYXRlU2VsZWN0SW5wdXQuaHRtbA=="> updateSelectInput ()</span> 来填充可用的变量。详情请参阅第 10.1 节。</p></li><li><p>创建一个应用程序，允许用户上传 CSV 文件，选择一个变量，绘制直方图，然后下载直方图。作为额外的挑战，允许用户从 <code>.png</code> 、 <code>.pdf</code> 和 <code>.svg</code> 输出格式中进行选择。</p></li><li><p>编写一个应用程序，允许用户使用 Ryan Timpe 的<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3J5YW50aW1wZS9icmlja3I="> brickr</span> 包从任何 <code>.png</code> 文件创建乐高马赛克。完成基本功能后，添加控件以允许用户选择马赛克的大小（以砖块为单位），并选择使用 <code>“universal”</code> 或 <code>“generic”</code> 颜色调色板。</p></li><li><p>第 9.3 节中的最后一个应用程序包含了一个大型的响应式表达式：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>tidied <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  out <span class="token operator">&lt;-</span> raw<span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  <span class="token keyword">if</span> <span class="token punctuation">(</span>input<span class="token operator">$</span>snake<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    names<span class="token punctuation">(</span>out<span class="token punctuation">)</span> <span class="token operator">&lt;-</span> janitor<span class="token operator">::</span>make_clean_names<span class="token punctuation">(</span>names<span class="token punctuation">(</span>out<span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>  <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>  <span class="token keyword">if</span> <span class="token punctuation">(</span>input<span class="token operator">$</span>empty<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    out <span class="token operator">&lt;-</span> janitor<span class="token operator">::</span>remove_empty<span class="token punctuation">(</span>out<span class="token punctuation">,</span> <span class="token string">"cols"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>  <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>  <span class="token keyword">if</span> <span class="token punctuation">(</span>input<span class="token operator">$</span>constant<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    out <span class="token operator">&lt;-</span> janitor<span class="token operator">::</span>remove_constant<span class="token punctuation">(</span>out<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>  <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="12"></td><td><pre></pre></td></tr><tr><td data-num="13"></td><td><pre>  out</pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr></table></figure><p>将其分解为多个部分，以便当 <code>input$empty</code> 发生变化时，不会重新运行 <code>janitor::make_clean_names()</code> 。</p></li></ol><h2 id="95-总结"><a class="anchor" href="#95-总结">#</a> 9.5 总结</h2><p>在本章中，您学习了如何使用 <code>fileInput()</code> 和 <code>downloadButton()</code> 来在用户之间传输文件。大多数挑战要么来自处理上传的文件，要么来自生成要下载的文件，因此我向您展示了如何处理一些常见的情况。如果我没有涵盖您在此处的特定挑战，那么您需要将您独特的创造力应用于问题中。</p><p>下一章将帮助您处理在处理用户提供的数据时遇到的一个常见挑战：您需要动态地调整用户界面以更好地适应数据。我将从一些易于理解且可以在许多情况下应用的简单技术开始，逐渐过渡到完全动态的用户界面。</p><h1 id="加关注"><a class="anchor" href="#加关注">#</a> 加关注</h1><p>关注公众号 “生信之巅”。</p><table align="center"><tr><td align="center"><img data-src="https://cdn.jsdelivr.net/gh/liaochenlanruo/cdn@master/img/social/生信之巅公众号.jpg" alt="生信之巅微信公众号" style="width:100px;height:100px;vertical-align:-20px;border-radius:0;margin-right:0;margin-bottom:5px;align:center"></td><td align="center"><img data-src="https://cdn.jsdelivr.net/gh/liaochenlanruo/cdn@master/img/social/小程序码.png" alt="生信之巅小程序码" style="width:100px;height:100px;vertical-align:-20px;border-radius:0;margin-left:0;margin-bottom:5px;align:center"></td></tr></table><p><font color="#FF0000"><ruby><b>敬告</b>：使用文中脚本请引用本文网址，请尊重本人的劳动成果，谢谢！<rt><b>Notice</b>: When you use the scripts in this article, please cite the link of this webpage. Thank you!</rt></ruby></font></p><div class="tags"><a href="/tags/%E7%BC%96%E7%A8%8B/" rel="tag"><i class="ic i-tag"></i> 编程</a> <a href="/tags/Shiny%E5%85%A5%E9%97%A8%E7%B3%BB%E5%88%97/" rel="tag"><i class="ic i-tag"></i> Shiny入门系列</a></div></div><footer><div class="meta"><span class="item"><span class="icon"><i class="ic i-calendar-check"></i> </span><span class="text">Edited on</span> <time title="Modified: 2024-04-27 21:52:48" itemprop="dateModified" datetime="2024-04-27T21:52:48+08:00">2024-04-27</time> </span><span id="post/72d6.html" class="item leancloud_visitors" data-flag-title="Shiny从入门到入定——9-上传和下载" title="Views"><span class="icon"><i class="ic i-eye"></i> </span><span class="text">Views</span> <span class="leancloud-visitors-count"></span> <span class="text">times</span></span></div><div class="reward"><button><i class="ic i-heartbeat"></i> Donate</button><p>Give me a cup of [coffee]~(￣▽￣)~*</p><div id="qr"><div><img data-src="/images/reward-wepays.jpg" alt="Hualin Liu WeChat Pay"><p>WeChat Pay</p></div><div><img data-src="/images/AliPays.jpg" alt="Hualin Liu Alipay"><p>Alipay</p></div></div></div><div id="copyright"><ul><li class="author"><strong>Post author: </strong>liaochenlanruo <i class="ic i-at"><em>@</em></i>了尘兰若的小坑</li><li class="link"><strong>Post link: </strong><a href="https://liaochenlanruo.gitee.io/post/72d6.html" title="Shiny从入门到入定——9-上传和下载">https://liaochenlanruo.gitee.io/post/72d6.html</a></li><li class="license"><strong>Copyright Notice: </strong>All articles in this blog are licensed under <span class="exturl" data-url="aHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLXNhLzQuMC9kZWVkLnpo"><i class="ic i-creative-commons"><em>(CC)</em></i>BY-NC-SA</span> unless stating additionally.</li></ul></div></footer></article></div><div class="post-nav"><div class="item left"><a href="/post/6561.html" itemprop="url" rel="prev" data-background-image="https:&#x2F;&#x2F;cdn.jsdelivr.net&#x2F;gh&#x2F;liaochenlanruo&#x2F;cdn@master&#x2F;img&#x2F;custom&#x2F;bgs&#x2F;thumb_208.webp" title="Shiny从入门到入定——8-用户反馈"><span class="type">Previous Post</span> <span class="category"><i class="ic i-flag"></i> IT</span><h3>Shiny从入门到入定——8-用户反馈</h3></a></div><div class="item right"><a href="/post/e6be.html" itemprop="url" rel="next" data-background-image="https:&#x2F;&#x2F;cdn.jsdelivr.net&#x2F;gh&#x2F;liaochenlanruo&#x2F;cdn@master&#x2F;img&#x2F;custom&#x2F;bgs&#x2F;thumb_210.webp" title="Shiny从入门到入定——10-动态UI"><span class="type">Next Post</span> <span class="category"><i class="ic i-flag"></i> IT</span><h3>Shiny从入门到入定——10-动态UI</h3></a></div></div><div class="wrap" id="comments"></div></div><div id="sidebar"><div class="inner"><div class="panels"><div class="inner"><div class="contents panel pjax" data-title="Contents"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#9-%E4%B8%8A%E4%BC%A0%E5%92%8C%E4%B8%8B%E8%BD%BD"><span class="toc-number">1.</span> <span class="toc-text">9 上传和下载</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#91-%E4%B8%8A%E4%BC%A0"><span class="toc-number">1.1.</span> <span class="toc-text">9.1 上传</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#911-%E7%94%A8%E6%88%B7%E7%95%8C%E9%9D%A2-ui"><span class="toc-number">1.1.1.</span> <span class="toc-text">9.1.1 用户界面 UI</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#912-%E6%9C%8D%E5%8A%A1%E5%99%A8-server"><span class="toc-number">1.1.2.</span> <span class="toc-text">9.1.2 服务器 server</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#913-%E4%B8%8A%E4%BC%A0%E6%95%B0%E6%8D%AE"><span class="toc-number">1.1.3.</span> <span class="toc-text">9.1.3 上传数据</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#92-%E4%B8%8B%E8%BD%BD"><span class="toc-number">1.2.</span> <span class="toc-text">9.2 下载</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#921-%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C"><span class="toc-number">1.2.1.</span> <span class="toc-text">9.2.1 基本操作</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#922-%E4%B8%8B%E8%BD%BD%E6%95%B0%E6%8D%AE"><span class="toc-number">1.2.2.</span> <span class="toc-text">9.2.2 下载数据</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#923-%E4%B8%8B%E8%BD%BD%E6%8A%A5%E5%91%8A"><span class="toc-number">1.2.3.</span> <span class="toc-text">9.2.3 下载报告</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#93-%E6%A1%88%E4%BE%8B%E7%A0%94%E7%A9%B6"><span class="toc-number">1.3.</span> <span class="toc-text">9.3 案例研究</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#94-%E7%BB%83%E4%B9%A0"><span class="toc-number">1.4.</span> <span class="toc-text">9.4 练习</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#95-%E6%80%BB%E7%BB%93"><span class="toc-number">1.5.</span> <span class="toc-text">9.5 总结</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%8A%A0%E5%85%B3%E6%B3%A8"><span class="toc-number">2.</span> <span class="toc-text">加关注</span></a></li></ol></div><div class="related panel pjax" data-title="Related"><ul><li><a href="/post/10103.html" rel="bookmark" title="perl循环调用python爬虫批量下载喜马拉雅音频">perl循环调用python爬虫批量下载喜马拉雅音频</a></li><li><a href="/post/2159.html" rel="bookmark" title="syncthing搭建自己的同步云网盘">syncthing搭建自己的同步云网盘</a></li><li><a href="/post/50435.html" rel="bookmark" title="JAVA jre 安装或升级1603 error解决办法">JAVA jre 安装或升级1603 error解决办法</a></li><li><a href="/post/aa20.html" rel="bookmark" title="获取下一版本的内部预览版windows系统">获取下一版本的内部预览版windows系统</a></li><li><a href="/post/c78e.html" rel="bookmark" title="windows 10 中文乱码解决方案">windows 10 中文乱码解决方案</a></li><li><a href="/post/6bf1.html" rel="bookmark" title="Ubuntu open-mpi运行错误">Ubuntu open-mpi运行错误</a></li><li><a href="/post/f1a4.html" rel="bookmark" title="Github操作">Github操作</a></li><li><a href="/post/f298.html" rel="bookmark" title="Wget 技巧">Wget 技巧</a></li><li><a href="/post/16e1.html" rel="bookmark" title="Perl捕获外部命令执行异常并继续运行后续代码">Perl捕获外部命令执行异常并继续运行后续代码</a></li><li><a href="/post/9092.html" rel="bookmark" title="Perl获取外部命令执行结果的输出">Perl获取外部命令执行结果的输出</a></li><li><a href="/post/9d1b.html" rel="bookmark" title="Linux骚操作">Linux骚操作</a></li><li><a href="/post/3f45.html" rel="bookmark" title="Perl处理可恶的Windows换行符">Perl处理可恶的Windows换行符</a></li><li><a href="/post/893d.html" rel="bookmark" title="命令模式下配置Linux网络">命令模式下配置Linux网络</a></li><li><a href="/post/ed43.html" rel="bookmark" title="Linux中使用tar将大文件压缩为多个小的压缩包">Linux中使用tar将大文件压缩为多个小的压缩包</a></li><li><a href="/post/54a0.html" rel="bookmark" title="WSL安装Docker避坑指北">WSL安装Docker避坑指北</a></li><li><a href="/post/4b9c.html" rel="bookmark" title="Shiny从入门到入定——0欢迎入坑">Shiny从入门到入定——0欢迎入坑</a></li><li><a href="/post/b376.html" rel="bookmark" title="Shiny从入门到入定——1.在小小的花园里面挖呀挖呀挖">Shiny从入门到入定——1.在小小的花园里面挖呀挖呀挖</a></li><li><a href="/post/5369.html" rel="bookmark" title="Shiny从入门到入定——2-在大大的花园里面挖呀挖呀挖">Shiny从入门到入定——2-在大大的花园里面挖呀挖呀挖</a></li><li><a href="/post/41c3.html" rel="bookmark" title="Shiny从入门到入定——3-在特别大的花园里面挖呀挖呀挖">Shiny从入门到入定——3-在特别大的花园里面挖呀挖呀挖</a></li><li><a href="/post/377b.html" rel="bookmark" title="Shiny从入门到入定——4-挖坑完毕之案例研究">Shiny从入门到入定——4-挖坑完毕之案例研究</a></li><li><a href="/post/83c3.html" rel="bookmark" title="Shiny从入门到入定——5-工作流">Shiny从入门到入定——5-工作流</a></li><li><a href="/post/6718.html" rel="bookmark" title="Shiny从入门到入定——6-布局、主题、HTML">Shiny从入门到入定——6-布局、主题、HTML</a></li><li><a href="/post/6685.html" rel="bookmark" title="Shiny从入门到入定——7-图形">Shiny从入门到入定——7-图形</a></li><li><a href="/post/6561.html" rel="bookmark" title="Shiny从入门到入定——8-用户反馈">Shiny从入门到入定——8-用户反馈</a></li><li class="active"><a href="/post/72d6.html" rel="bookmark" title="Shiny从入门到入定——9-上传和下载">Shiny从入门到入定——9-上传和下载</a></li><li><a href="/post/e6be.html" rel="bookmark" title="Shiny从入门到入定——10-动态UI">Shiny从入门到入定——10-动态UI</a></li><li><a href="/post/8eca.html" rel="bookmark" title="Shiny从入门到入定——11-书签">Shiny从入门到入定——11-书签</a></li><li><a href="/post/3709.html" rel="bookmark" title="Shiny从入门到入定——12-Tidy evaluation">Shiny从入门到入定——12-Tidy evaluation</a></li><li><a href="/post/5e3d.html" rel="bookmark" title="Shiny从入门到入定——13-Why reactivity?">Shiny从入门到入定——13-Why reactivity?</a></li><li><a href="/post/43fc.html" rel="bookmark" title="深入理解特征标准化：为何、如何及其重要性">深入理解特征标准化：为何、如何及其重要性</a></li></ul></div><div class="overview panel" data-title="Overview"><div class="author" itemprop="author" itemscope itemtype="http://schema.org/Person"><img class="image" itemprop="image" alt="Hualin Liu" data-src="/images/head.jpg"><p class="name" itemprop="name">Hualin Liu</p><div class="description" itemprop="description">分享微生物生物信息学分析方法，欢迎加入QQ群交流945751012，不接受群内广告！</div></div><nav class="state"><div class="item posts"><a href="/archives/"><span class="count">130</span> <span class="name">posts</span></a></div><div class="item categories"><a href="/categories/"><span class="count">12</span> <span class="name">categories</span></a></div><div class="item tags"><a href="/tags/"><span class="count">54</span> <span class="name">tags</span></a></div></nav><div class="social"><span class="exturl item github" data-url="aHR0cDovL2dpdGh1Yi5jb20vbGlhb2NoZW5sYW5ydW8=" title="http:&#x2F;&#x2F;github.com&#x2F;liaochenlanruo"><i class="ic i-github"></i></span> <span class="exturl item zhihu" data-url="aHR0cHM6Ly93d3cuemhpaHUuY29tL3Blb3BsZS9zdWFuLWxhLW1pYW4tcGlhbi10YW5nLWNpdW0=" title="https:&#x2F;&#x2F;www.zhihu.com&#x2F;people&#x2F;suan-la-mian-pian-tang-cium"><i class="ic i-zhihu"></i></span> <span class="exturl item weibo" data-url="aHR0cHM6Ly93ZWliby5jb20vdS8yNzE1MjEwOTIz" title="https:&#x2F;&#x2F;weibo.com&#x2F;u&#x2F;2715210923"><i class="ic i-weibo"></i></span> <span class="exturl item about" data-url="aHR0cHM6Ly93d3cubGlhb2NoZW5sYW5ydW8uZnVuL2Fib3V0" title="https:&#x2F;&#x2F;www.liaochenlanruo.fun&#x2F;about"><i class="ic i-address-card"></i></span> <span class="exturl item email" data-url="bWFpbHRvOmxpYW9jaGVubGFucnVvQHdlYm1haWwuaHphdS5lZHUuY24=" title="mailto:liaochenlanruo@webmail.hzau.edu.cn"><i class="ic i-envelope"></i></span> <span class="exturl item douban" data-url="aHR0cHM6Ly93d3cuZG91YmFuLmNvbS9wZW9wbGUvbGlhb2NoZW5sYW5ydW8vP19pPTczNDYxMjVRalo1QjZWLw==" title="https:&#x2F;&#x2F;www.douban.com&#x2F;people&#x2F;liaochenlanruo&#x2F;?_i&#x3D;7346125QjZ5B6V&#x2F;"><i class="ic i-douban"></i></span> <span class="exturl item google" data-url="aHR0cHM6Ly9zY2hvbGFyLmdvb2dsZS5jby5qcC9jaXRhdGlvbnM/dXNlcj1YYW9STk1RQUFBQUomaGw9amE=" title="https:&#x2F;&#x2F;scholar.google.co.jp&#x2F;citations?user&#x3D;XaoRNMQAAAAJ&amp;hl&#x3D;ja"><i class="ic i-google"></i></span></div><ul class="menu"><li class="item"><a href="/" rel="section"><i class="ic i-home"></i>Home</a></li><li class="item dropdown"><a href="javascript:void(0);"><i class="ic i-user"></i>About</a><ul class="submenu"><li class="item"><a href="/about/" rel="section"><i class="ic i-user"></i>About me</a></li><li class="item"><a href="/comment/" rel="section"><i class="ic i-envelope"></i>Comment</a></li></ul></li><li class="item dropdown"><a href="javascript:void(0);"><i class="ic i-feather"></i>Posts</a><ul class="submenu"><li class="item"><a href="/archives/" rel="section"><i class="ic i-list-alt"></i>Archives</a></li><li class="item"><a href="/categories/" rel="section"><i class="ic i-th"></i>Categories</a></li><li class="item"><a href="/tags/" rel="section"><i class="ic i-tags"></i>Tags</a></li></ul></li><li class="item dropdown"><a href="javascript:void(0);"><i class="ic i-list-ol"></i>List</a><ul class="submenu"><li class="item"><a href="/photos/" rel="section"><i class="ic i-person"></i>Photos</a></li><li class="item"><a href="/skip/gallery/" rel="section"><i class="ic i-cloud"></i>Gallery</a></li><li class="item"><a href="/skip/box/" rel="section"><i class="ic i-magic"></i>Box</a></li><li class="item"><span class="exturl" data-url="aHR0cHM6Ly9saWFvY2hlbmxhbnJ1by5mdW4vcmVmZXJlbmNlLw=="><i class="ic i-th"></i>cheatsheets</span></li></ul></li><li class="item"><a href="/donate/" rel="section"><i class="ic i-coffee"></i>Donate</a></li><li class="item"><a href="/friends/" rel="section"><i class="ic i-heart"></i>Friends</a></li><li class="item"><span class="exturl" data-url="aHR0cHM6Ly9saWFvY2hlbmxhbnJ1by5naXRodWIuaW8="><i class="ic i-external-link-alt"></i>中文版</span></li><li class="item"><span class="exturl" data-url="aHR0cHM6Ly90cmF2ZWxsaW5ncy5saW5r"><i class="ic i-forward"></i>Travelling</span></li></ul></div></div></div><ul id="quick"><li class="prev pjax"><a href="/post/6561.html" rel="prev" title="Previous Post"><i class="ic i-chevron-left"></i></a></li><li class="up"><i class="ic i-arrow-up"></i></li><li class="down"><i class="ic i-arrow-down"></i></li><li class="next pjax"><a href="/post/e6be.html" rel="next" title="Next Post"><i class="ic i-chevron-right"></i></a></li><li class="percent"></li></ul></div></div><div class="dimmer"></div></div></main><footer id="footer"><div class="inner"><div class="widgets"></div><div class="status"><div class="copyright">&copy; 2018 – <span itemprop="copyrightYear">2024</span> <span class="with-love"><i class="ic i-sakura rotate"></i> </span><span class="author" itemprop="copyrightHolder">Hualin Liu @ ResearchGo</span></div><div class="count"><span class="post-meta-item-icon"><i class="ic i-chart-area"></i> </span><span title="Symbols count total">788k words</span> <span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="ic i-coffee"></i> </span><span title="Reading time total">11:57</span><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="ic i-info-circle"></i> </span><span style="padding-left:1px"><span id="busuanzi_container_site_pv">Total visits: <span id="busuanzi_value_site_pv" style="color:#f99"></span> times</span> <span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="ic i-user"></i> </span><span style="padding-left:1px"><span id="busuanzi_container_site_uv">Total visitors: <span id="busuanzi_value_site_uv" style="color:#f99"></span> people</span></span></span></div><div class="powered-by">Powered by <span class="exturl" data-url="aHR0cHM6Ly9oZXhvLmlv">Hexo</span> & Theme.<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2FtZWhpbWUvaGV4by10aGVtZS1zaG9rYQ==">Shoka</span></div></div></div></footer></div><script data-config type="text/javascript">var LOCAL={path:"post/72d6.html",favicon:{show:"（●´3｀●）Goooood",hide:"(´Д｀)Booooom"},search:{placeholder:"Search for Posts",empty:"We didn't find any results for the search: ${query}",stats:"${hits} results found in ${time} ms"},valine:!0,fancybox:!0,copyright:'Copied to clipboard successfully! <br> All articles in this blog are licensed under <i class="ic i-creative-commons"></i>BY-NC-SA.',ignores:[function(e){return e.includes("#")},function(e){return new RegExp(LOCAL.path+"$").test(e)}]}</script><script src="https://cdn.polyfill.io/v2/polyfill.js"></script><script src="//cdn.jsdelivr.net/combine/npm/pace-js@1.0.2/pace.min.js,npm/pjax@0.2.8/pjax.min.js,npm/whatwg-fetch@3.4.0/dist/fetch.umd.min.js,npm/animejs@3.2.0/lib/anime.min.js,npm/algoliasearch@4/dist/algoliasearch-lite.umd.js,npm/instantsearch.js@4/dist/instantsearch.production.min.js,npm/lozad@1/dist/lozad.min.js,npm/quicklink@2/dist/quicklink.umd.js"></script><script src="/js/app.js?v=0.2.5"></script><script data-pjax>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?d211830a796ceefb38a141fde14e4d5a";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script></body></html>